package filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.beans.factory.access.BeanFactoryReference;
import org.springframework.context.ApplicationContext;
import org.springframework.context.access.ContextSingletonBeanFactoryLocator;

import security.CoreSecurityManager;
import util.CookieUtil;
import bean.CookieInfoBean;
import bean.ResultMessageObject;
import bean.UserObject;
import biz.LoginBiz;

/**
 * 
 * Base class for request filter
 * 
 * @author Ben
 * 
 */
public abstract class AbstractCheckFilter implements Filter {
	private static final Log log = LogFactory.getLog(AbstractCheckFilter.class);

	//ignore list
	private String[] ignorePattern;

	/*
	 * (非 Javadoc)
	 * 
	 * @see javax.servlet.Filter#destroy()
	 */
	public void destroy() {
	}

	/*
	 * (非 Javadoc)
	 * 
	 * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse,
	 * javax.servlet.FilterChain)
	 */
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

		if (log.isDebugEnabled()) {
			log.debug("filter ring:[" + ((HttpServletRequest) request).getRequestURI() + "]");
		}

		String requestPath = ((HttpServletRequest) request).getRequestURI();

		boolean ignore = false;
		boolean sessionFlg = false;

		for (String s : ignorePattern) {
			if (requestPath.matches(s)) {
				if (log.isDebugEnabled()) {
					log.debug("ignore:" + requestPath + "[pattern:" + s + "]");
				}
				ignore = true;
				break;
			}
		}
		HttpServletRequest httpRequest = (HttpServletRequest) request;

		// if the request url matches anyone of the ignore url list,
		// pass through.
		if (ignore) {
			chain.doFilter(request, response);
		} else {
			HttpSession session = httpRequest.getSession(true);
			HttpServletResponse rs = (HttpServletResponse) response;
			CookieInfoBean cookieInfo = CookieUtil.createCookieInfo(httpRequest.getCookies());
			// validate the request
			if ((!validate((HttpServletRequest) request, (HttpServletResponse) response, chain))) {
				// timeout case
				try {
					// create new session
					sessionFlg = createNewSession(cookieInfo, httpRequest, session);
					if (!sessionFlg) {
						setResultMessage("Z3101W0003", "Z3101W0105", false, session, rs);
					} else {
						setResultMessage("Z3101W0002", "Z3101W0102", false, session, rs);
					}
				} catch (Exception e) {
					log.error("system error", e);
					setResultMessage("Z3101W0001", "Z3101W0101", true, session, rs);
				}
			}
			// if pass the validation
			else {
				UserObject uo = (UserObject) session.getAttribute(UserObject.USER_OBJECT_KEY);
				if (uo != null && StringUtils.equals(cookieInfo.getUserId(), uo.getUserId())) {
					// auth check to avoid the illegal access
					boolean authCheckResult = CoreSecurityManager.authorizeURL(httpRequest);
					// if it is a illegal access
					if (!authCheckResult) {
						setResultMessage("Z3101W0005", "Z3101W0106", false, session, rs);
					} else {
						chain.doFilter(request, response);
					}
				} else {
					setResultMessage("Z3101W0003", "Z3101W0103", false, session, rs);
				}
			}
		}
	}

	/**
	 * Create new session
	 * 
	 * @param cookieInfo cookie information
	 * @param request HttpServletRequest
	 * @param session HttpSession
	 * @return boolean
	 */
	public boolean createNewSession(CookieInfoBean cookieInfo, HttpServletRequest request, HttpSession session) {
		// clear session attribute
		session.removeAttribute(UserObject.USER_OBJECT_KEY);
		BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance();
		BeanFactoryReference ref = locator.useBeanFactory("context");
		ApplicationContext context = (ApplicationContext) ref.getFactory();

		LoginBiz loginBiz = (LoginBiz) context.getBean("loginBiz");

		// get user information
		UserObject uo = loginBiz.selectUserObject(cookieInfo.getUserId(), cookieInfo.getPassword());
		if (uo == null) {
			return false;
		}
		session.setAttribute(UserObject.USER_OBJECT_KEY, uo);

		if (log.isDebugEnabled()) {
			log.debug("new session created:" + ToStringBuilder.reflectionToString(uo, ToStringStyle.MULTI_LINE_STYLE));
		}

		return true;
	}

	/**
	 * Set result message
	 * 
	 * @param titleCode
	 * @param code
	 * @param syserrorFlg
	 * @param session
	 * @param response
	 * @throws IOException
	 */
	public void setResultMessage(String titleCode, String code, boolean syserrorFlg, HttpSession session, HttpServletResponse response)
			throws IOException {
		ResultMessageObject result = new ResultMessageObject();
		result.setTitleCode(titleCode);
		result.setCode(code);
		result.setSyserror(syserrorFlg);
		session.setAttribute(ResultMessageObject.RESULT_MESSAGE_KEY, result);
		response.sendRedirect("ErrorHandle.do");
	}

	/**
	 * check process。
	 * The result for check, "true" means process continue, "false" means process break。
	 * 
	 * @param request HttpServletRequest
	 * @param response HttpServletResponse
	 * @param chain FilterChain
	 * @return The result for check
	 */
	public abstract boolean validate(HttpServletRequest request, HttpServletResponse response, FilterChain chain);

	/*
	 * (非 Javadoc)
	 * 
	 * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
	 */
	public void init(FilterConfig filterConfig) throws ServletException {
		// get ignore list
		String patterns = filterConfig.getInitParameter("ignorePattern");
		this.ignorePattern = patterns != null ? StringUtils.split(patterns, ",") : new String[] { "" };

		filterConfig.getServletContext();
	}

}
